//
// This code is to be used for evaluation of the CS2J tool ONLY.
//
// This file was translated from C# to Java by CS2J (http://www.cs2j.com).
//
// For more information about CS2J please contact info@twigletsoftware.com
//
// Translated by CS2J (http://www.cs2j.com): 2013/1/12 14:27:24
//

package ms.sudoku.technique;

import java.util.Arrays;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

import ms.sudoku.PuzzleState;
import ms.sudoku.util.IntSet;

public abstract class HiddenSubsetTechnique extends EliminationTechnique {
	private int _subsetSize;
	private int[] _foundLocations;

	protected HiddenSubsetTechnique(int subsetSize) {
		if (subsetSize < 2)
			throw new RuntimeException("subsetSize");

		_subsetSize = subsetSize;
		_foundLocations = new int[_subsetSize];
	}

	// optimization, rather than allocating on each call
	private int eliminateHiddenSubsets(IntSet[] possibleNumbers, boolean exitEarlyWhenSoleFound,
			AtomicBoolean exitedEarly) {
		Integer changesMade = 0;
		exitedEarly.set(false);
		Integer numLocations;
		int[] foundLocations = _foundLocations;
		for (Integer i = 0; i < possibleNumbers.length; i++) {
			// optimization, rather than allocating on each call
			Integer numPossible = possibleNumbers[i].size();
			if (numPossible >= _subsetSize) {
				for (int[] combination : new CombinationIterator(_subsetSize, possibleNumbers[i].toArray())) {
					numLocations = 0;
					foundLocations[numLocations++] = i;
					for (Integer j = i + 1; j < possibleNumbers.length && numLocations < foundLocations.length; j++) {
						if (possibleNumbers[j].containsAll(combination)) {
							foundLocations[numLocations++] = j;
						}

					}
					if (numLocations == foundLocations.length) {
						Boolean isValidHidden = true;
						for (Integer j = 0; j < possibleNumbers.length && isValidHidden; j++) {
							Boolean isFoundLocation = Arrays.binarySearch(foundLocations, j) >= 0;
							if (!isFoundLocation && possibleNumbers[j].containsAny(combination)) {
								isValidHidden = false;
								break;
							}

						}
						if (isValidHidden) {
							for (Integer foundLoc : foundLocations) {
								IntSet possibleNumbersForLoc = possibleNumbers[foundLoc];
								for (int n : possibleNumbersForLoc) {
									if (Arrays.binarySearch(combination, n) < 0) {
										possibleNumbersForLoc.remove(n);
										changesMade++;
									}

								}
								if (exitEarlyWhenSoleFound && possibleNumbersForLoc.size() == 1) {
									exitedEarly.set(true);
									return changesMade;
								}

							}
							break;
						}

					}

				}
			}

		}
		return changesMade;
	}

	// optimization, rather than allocating on each call
	public boolean execute(PuzzleState state, boolean exitEarlyWhenSoleFound, IntSet[][] possibleNumbers,
			AtomicInteger numberOfChanges, AtomicBoolean exitedEarly) {
		numberOfChanges.set(0);
		exitedEarly.set(false);
		IntSet[] arrays = new IntSet[state.getGridSize()];
		for (Integer i = 0; i < state.getGridSize(); i++) {
			getRowPossibleNumbers(possibleNumbers, i, arrays);
			AtomicBoolean rowExitedEarly = new AtomicBoolean();
			numberOfChanges.addAndGet(eliminateHiddenSubsets(arrays, exitEarlyWhenSoleFound, rowExitedEarly));
			exitedEarly.set(rowExitedEarly.get());
			if (exitedEarly.get())
				return false;

			getColumnPossibleNumbers(possibleNumbers, i, arrays);
			AtomicBoolean colExitedEarly = new AtomicBoolean();
			numberOfChanges.addAndGet(eliminateHiddenSubsets(arrays, exitEarlyWhenSoleFound, colExitedEarly));
			exitedEarly.set(colExitedEarly.get());
			if (exitedEarly.get())
				return false;

			getBoxPossibleNumbers(state, possibleNumbers, i, arrays);
			AtomicBoolean boxExitedEarly = new AtomicBoolean();
			numberOfChanges.addAndGet(eliminateHiddenSubsets(arrays, exitEarlyWhenSoleFound, boxExitedEarly));
			exitedEarly.set(boxExitedEarly.get());
			if (exitedEarly.get())
				return false;

		}
		return numberOfChanges.get() != 0;
	}

}
